package com.wpen.unit.core;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import com.wpen.unit.core.service.BatchScheduleHandler;
import org.springframework.beans.factory.DisposableBean;

import com.wpen.unit.core.ddo.TriggerDo;
import com.wpen.unit.core.service.TriggerPoolHelper;
import com.wpen.unit.utils.DateUtil;
import com.wpen.unit.utils.SpringUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * batch批次调度
 * 
 * @author Wang Peng
 * @date 2022-12-06
 */
@Slf4j
public class BatchScheduleWorker extends ZyxWorker implements DisposableBean {
    private static final BatchScheduleWorker instance = new BatchScheduleWorker();

    private BatchScheduleWorker() {
    }

    public static BatchScheduleWorker getInstance() {
        return instance;
    }

    /* 循环读取间隔时间，扫描库的间隔时间，单位毫秒 */
    private final long loopRead = 5000;
    private final int onceReadSize = 200;

    private Thread batchObserver;
    private Thread batchDelayExec;
    private volatile boolean observerRunning = true;
    private volatile boolean delayRunning = true;

    private volatile static Map<Integer, List<TriggerDo>> batchDelayData = new ConcurrentHashMap<>();

    public void start() {
        log.info(this.getClass().getName() + " start...");

        batchObserver = new Thread(new Runnable() {
            // 常驻在线子线程
            @Override
            public void run() {
                // 检测是否启动完成
                checkAppStarted();
                // 批次扫描线程
                while (observerRunning) {
                    // 批次日期，，采用自然日
                    String acctDate = DateUtil.getAcctDate();
                    // 每次执行耗时，毫秒
                    long start = System.currentTimeMillis();
                    try {
                        BatchScheduleHandler scheduleHandler = SpringUtil.getBean(BatchScheduleHandler.class);
                        scheduleHandler.scheduleHandle(acctDate, start, onceReadSize, loopRead, batchDelayData);
                    } catch (Exception e) {
                        log.error("batchObserverThread Exception>" + e.getMessage(), e);
                    } finally {
                        try {
                            long cost = System.currentTimeMillis() - start;
                            TimeUnit.MILLISECONDS.sleep(loopRead - cost);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
        batchObserver.setDaemon(true);
        batchObserver.setName("schedule-batch-observer");
        batchObserver.start();
        log.info(batchObserver.getName() + " running...");

        batchDelayExec = new Thread(new Runnable() {
            @Override
            public void run() {
                // 检测是否启动完成
                checkAppStarted();
                while (delayRunning) {
                    try {
                        TimeUnit.MILLISECONDS.sleep(1000 - System.currentTimeMillis() % 1000);
                    } catch (InterruptedException e) {
                        log.error(e.getMessage(), e);
                    }
                    try {
                        List<TriggerDo> execItems = new ArrayList<>();
                        // 避免处理耗时太长，跨过刻度，向前校验一个刻度；
                        int nowSecond = Calendar.getInstance().get(Calendar.SECOND);
                        // 3秒内待执行的必须全部发送执行完成
                        for (int i = 0; i < 3; i++) {
                            List<TriggerDo> tmpData = batchDelayData.remove((nowSecond + 60 - i) % 60);
                            if (tmpData != null) {
                                execItems.addAll(tmpData);
                            }
                        }
                        if (log.isDebugEnabled()) {
                            log.debug("sdp-job to trigger batchs : " + nowSecond + " = " + Arrays.asList(execItems));
                        }
                        if (execItems.size() > 0) {
                            for (TriggerDo triggerDo : execItems) {
                                TriggerPoolHelper triggerPoolHelper = SpringUtil.getBean(TriggerPoolHelper.class);
                                triggerPoolHelper.triggerBatch(triggerDo);
                            }
                        }
                    } catch (Exception e) {
                        log.error("batchDelayExecThread Exception>" + e.getMessage(), e);
                    }
                }
            }
        });
        batchDelayExec.setDaemon(true);
        batchDelayExec.setName("schedule-batch-delay-exec");
        batchDelayExec.start();
    }

    public void destroy() {
        observerRunning = false;
        delayRunning = false;
        log.info(this.getClass().getName() + " bye..");
    }

}
